/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.openide.explorer.propertysheet.editors; import java.awt.*; import java.awt.event.*; import java.beans.*; import java.io.*; import java.util.*; import java.net.URL; import java.util.ResourceBundle; import javax.swing.*; import javax.swing.filechooser.FileFilter; import javax.swing.border.*; import org.openide.*; import org.openide.loaders.*; import org.openide.nodes.*; import org.openide.util.actions.CallableSystemAction; import org.openide.util.HelpCtx; import org.openide.explorer.propertysheet.editors.EnhancedCustomPropertyEditor; /** * PropertyEditor for Icons. Depends on existing DataObject for images. * Images must be represented by some DataObject which returns itselv * as cookie, and has image file as a primary file. File extensions * for images is specified in isImage method. * * @author Jan Jancura */ public class IconEditor extends Object implements PropertyEditor, XMLPropertyEditor { public static final int TYPE_URL = 1; public static final int TYPE_FILE = 2; public static final int TYPE_CLASSPATH = 3; static final String URL_PREFIX = "URL"; // NOI18N static final String FILE_PREFIX = "File"; // NOI18N static final String CLASSPATH_PREFIX = "Classpath"; // NOI18N /** Standart variable for localisation. */ static java.util.ResourceBundle bundle = org.openide.util.NbBundle.getBundle ( IconEditor.class); public static boolean isImage (String s) { s = s.toLowerCase (); return s.endsWith (".jpg") || s.endsWith (".gif") || // NOI18N s.endsWith (".jpeg") || s.endsWith (".jpe") || // NOI18N s.equals ("jpg") || s.equals ("gif") || // NOI18N s.equals ("jpeg") || s.equals ("jpe"); // NOI18N } static String convert (String s) { StringTokenizer st = new StringTokenizer (s, "\\"); // NOI18N StringBuffer sb = new StringBuffer (); if (st.hasMoreElements ()) { sb.append (st.nextElement ()); while (st.hasMoreElements ()) sb.append ("\\\\").append (st.nextElement ()); // NOI18N } return new String (sb); } // variables ................................................................................. private Icon icon; private PropertyChangeSupport support; // init ....................................................................................... public IconEditor() { support = new PropertyChangeSupport (this); } // Special access methods...................................................................... /** @return the type of image source - one of TYPE_CLASSPATH, TYPE_FILE, TYPE_URL */ public int getSourceType () { if (getValue () == null) return TYPE_FILE; try { NbImageIcon ii = (NbImageIcon)getValue (); return ii.type; } catch (ClassCastException e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace (); // NOI18N return TYPE_FILE; } } /** @return the name of image's source - depending on the type it can be a URL, file name or * resource path to the image on classpath */ public String getSourceName () { if (getValue () == null) return null; try { NbImageIcon ii = (NbImageIcon)getValue (); return ii.name; } catch (ClassCastException e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace (); // NOI18N return null; } } // PropertyEditor methods ..................................................................... /** * @return The value of the property. Builtin types such as "int" will * be wrapped as the corresponding object type such as "java.lang.Integer". */ public Object getValue () { return icon; } /** * Set (or change) the object that is to be edited. Builtin types such * as "int" must be wrapped as the corresponding object type such as * "java.lang.Integer". * * @param value The new target object to be edited. Note that this * object should not be modified by the PropertyEditor, rather * the PropertyEditor should create a new object to hold any * modified value. */ public void setValue (Object object) { Icon old = icon; icon = (Icon) object; support.firePropertyChange ("value", old, icon); // NOI18N } /** * @return The property value as a human editable string. * <p> Returns null if the value can't be expressed as an editable string. * <p> If a non-null value is returned, then the PropertyEditor should * be prepared to parse that string back in setAsText(). */ public String getAsText () { if (getValue () == null) return "null"; // NOI18N try { NbImageIcon ii = (NbImageIcon)getValue (); switch (ii.type) { case TYPE_URL: return URL_PREFIX + ": " + ii.name; // NOI18N case TYPE_FILE: return FILE_PREFIX + ": " + ii.name; // NOI18N case TYPE_CLASSPATH: return CLASSPATH_PREFIX + ": " + ii.name; // NOI18N } } catch (ClassCastException e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace (); // NOI18N } return null; } /** * Set the property value by parsing a given String. May raise * java.lang.IllegalArgumentException if either the String is * badly formatted or if this kind of property can't be expressed * as text. * @param text The string to be parsed. */ public void setAsText (String string) throws IllegalArgumentException { NbImageIcon ii; try { if (string.startsWith (FILE_PREFIX)) { String s = string.substring (FILE_PREFIX.length () + 1).trim (); ii = new NbImageIcon (s); ii.type = TYPE_FILE; ii.name = s; } else if (string.startsWith (CLASSPATH_PREFIX)) { String s = string.substring (CLASSPATH_PREFIX.length () + 1).trim (); URL url = TopManager.getDefault ().currentClassLoader ().getResource (s); ii = new NbImageIcon (url); ii.type = TYPE_CLASSPATH; ii.name = s; } else if (string.startsWith (URL_PREFIX)) { String s = string.substring (URL_PREFIX.length () + 1).trim (); URL url = new URL (s); ii = new NbImageIcon (url); ii.type = TYPE_URL; ii.name = s; } else { ii = new NbImageIcon (string.trim ()); ii.type = TYPE_FILE; ii.name = string; } setValue (ii); } catch (Exception e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace (); // NOI18N throw new IllegalArgumentException (e.toString ()); } } /** * This method is intended for use when generating Java code to set * the value of the property. It should return a fragment of Java code * that can be used to initialize a variable with the current property * value. * <p> * Example results are "2", "new Color(127,127,34)", "Color.orange", etc. * * @return A fragment of Java code representing an initializer for the * current value. */ public String getJavaInitializationString () { if (getValue () == null) return "null"; // NOI18N try { NbImageIcon ii = (NbImageIcon)getValue (); switch (ii.type) { case TYPE_URL: return "new javax.swing.JLabel () {\n" + // NOI18N " public javax.swing.Icon getIcon () {\n" + // NOI18N " try {\n" + // NOI18N " return new javax.swing.ImageIcon (\n" + // NOI18N " new java.net.URL (\"" + convert (ii.name) + "\")\n" + // NOI18N " );\n" + // NOI18N " } catch (java.net.MalformedURLException e) {\n" + // NOI18N " }\n" + // NOI18N " return null;\n" + // NOI18N " }\n" + // NOI18N "}.getIcon ()"; // NOI18N case TYPE_FILE: return "new javax.swing.ImageIcon (\"" + convert (ii.name) + "\")"; // NOI18N case TYPE_CLASSPATH: return "new javax.swing.ImageIcon (getClass ().getResource (\"" + convert (ii.name) + "\"))"; // NOI18N } } catch (ClassCastException e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace (); // NOI18N } return "null"; // NOI18N } /** * If the property value must be one of a set of known tagged values, * then this method should return an array of the tags. This can * be used to represent (for example) enum values. If a PropertyEditor * supports tags, then it should support the use of setAsText with * a tag value as a way of setting the value and the use of getAsText * to identify the current value. * * @return The tag values for this property. May be null if this * property cannot be represented as a tagged value. * */ public String[] getTags () { return null; } /** * @return True if the class will honor the paintValue method. */ public boolean isPaintable () { return false; } /** * Paint a representation of the value into a given area of screen * real estate. Note that the propertyEditor is responsible for doing * its own clipping so that it fits into the given rectangle. * <p> * If the PropertyEditor doesn't honor paint requests (see isPaintable) * this method should be a silent noop. * <p> * The given Graphics object will have the default font, color, etc of * the parent container. The PropertyEditor may change graphics attributes * such as font and color and doesn't need to restore the old values. * * @param gfx Graphics object to paint into. * @param box Rectangle within graphics object into which we should paint. */ public void paintValue (Graphics g, Rectangle rectangle) { } /** * @return True if the propertyEditor can provide a custom editor. */ public boolean supportsCustomEditor () { return true; } /** * A PropertyEditor may choose to make available a full custom Component * that edits its property value. It is the responsibility of the * PropertyEditor to hook itself up to its editor Component itself and * to report property value changes by firing a PropertyChange event. * <P> * The higher-level code that calls getCustomEditor may either embed * the Component in some larger property sheet, or it may put it in * its own individual dialog, or ... * * @return A java.awt.Component that will allow a human to directly * edit the current property value. May be null if this is * not supported. */ public java.awt.Component getCustomEditor () { return new IconPanel (); } /** * Register a listener for the PropertyChange event. When a * PropertyEditor changes its value it should fire a PropertyChange * event on all registered PropertyChangeListeners, specifying the * null value for the property name and itself as the source. * * @param listener An object to be invoked when a PropertyChange * event is fired. */ public void addPropertyChangeListener (PropertyChangeListener propertyChangeListener) { support.addPropertyChangeListener (propertyChangeListener); } /** * Remove a listener for the PropertyChange event. * * @param listener The PropertyChange listener to be removed. */ public void removePropertyChangeListener (PropertyChangeListener propertyChangeListener) { support.removePropertyChangeListener (propertyChangeListener); } // innerclasses ............................................................... public static class NbImageIcon extends ImageIcon implements Externalizable { /** generated Serialized Version UID */ static final long serialVersionUID = 7018807466471349466L; int type; String name; public NbImageIcon () { } NbImageIcon (URL url) { super (url); type = TYPE_URL; } NbImageIcon (String file) { super (file); type = TYPE_FILE; } String getName () { return name; } public void writeExternal (ObjectOutput oo) throws IOException { oo.writeObject (new Integer (type)); oo.writeObject (name); } public void readExternal (ObjectInput in) throws IOException, ClassNotFoundException { type = ((Integer)in.readObject ()).intValue (); name = (String) in.readObject (); ImageIcon ii = null; switch (type) { case TYPE_URL: try { ii = new ImageIcon (new URL (name)); } catch (java.net.MalformedURLException e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace (); // NOI18N return; } break; case TYPE_FILE: ii = new ImageIcon (name); break; case TYPE_CLASSPATH: ii = new ImageIcon (TopManager.getDefault ().currentClassLoader ().getResource (name)); break; } setImage (ii.getImage ()); } } class IconPanel extends JPanel implements EnhancedCustomPropertyEditor { JRadioButton rbUrl, rbFile, rbClasspath, rbNoPicture; JTextField tfName; JButton bSelect; JScrollPane spImage; static final long serialVersionUID =-6904264999063788703L; IconPanel () { // visual components ............................................. JLabel lab; setLayout (new BorderLayout (6, 6)); setBorder (new EmptyBorder (6, 6, 6, 6)); JPanel p = new JPanel (new BorderLayout (3, 3)); JPanel p1 = new JPanel (new BorderLayout ()); p1.setBorder (new TitledBorder (new EtchedBorder (), bundle.getString ("CTL_ImageSourceType"))); JPanel p2 = new JPanel (); p2.setBorder (new EmptyBorder (0, 3, 0, 3)); GridBagLayout l = new GridBagLayout (); GridBagConstraints c = new GridBagConstraints (); p2.setLayout (l); c.anchor = GridBagConstraints.WEST; p2.add (rbUrl = new JRadioButton (bundle.getString ("CTL_URL"))); c.gridwidth = 1; l.setConstraints (rbUrl, c); p2.add (lab = new JLabel (bundle.getString ("CTL_URLExample"))); c.gridwidth = GridBagConstraints.REMAINDER; l.setConstraints (lab, c); p2.add (rbFile = new JRadioButton (bundle.getString ("CTL_File"))); c.gridwidth = 1; l.setConstraints (rbFile, c); p2.add (lab = new JLabel (bundle.getString ("CTL_FileExample"))); c.gridwidth = GridBagConstraints.REMAINDER; l.setConstraints (lab, c); p2.add (rbClasspath = new JRadioButton (bundle.getString ("CTL_Classpath"))); c.gridwidth = 1; l.setConstraints (rbClasspath, c); p2.add (lab = new JLabel (bundle.getString ("CTL_ClasspathExample"))); c.gridwidth = GridBagConstraints.REMAINDER; l.setConstraints (lab, c); p2.add (rbNoPicture = new JRadioButton (bundle.getString ("CTL_NoPicture"))); c.gridwidth = 1; l.setConstraints (rbNoPicture, c); p2.add (lab = new JLabel (bundle.getString ("CTL_Null"))); c.gridwidth = GridBagConstraints.REMAINDER; l.setConstraints (lab, c); ButtonGroup bg = new ButtonGroup (); bg.add (rbUrl); bg.add (rbFile); bg.add (rbClasspath); bg.add (rbNoPicture); rbUrl.setSelected (true); p1.add (p2, "West"); // NOI18N p.add (p1, "North"); // NOI18N p1 = new JPanel (new BorderLayout (6, 6)); p1.add (new JLabel (bundle.getString ("CTL_ImageSourceName")), "West"); p1.add (tfName = new JTextField (), "Center"); // NOI18N p1.add (bSelect = new JButton ("..."), "East"); // NOI18N bSelect.setEnabled (false); p.add (p1, "South"); // NOI18N add (p, "North"); // NOI18N spImage = new JScrollPane () { public Dimension getPreferredSize () { return new Dimension (60, 60); } }; add (spImage, "Center"); // NOI18N // listeners ................................................. tfName.addActionListener (new ActionListener () { public void actionPerformed (ActionEvent e) { setValue (); } }); rbUrl.addActionListener (new ActionListener () { public void actionPerformed (ActionEvent e) { bSelect.setEnabled (false); tfName.setEnabled (true); setValue (); } }); rbFile.addActionListener (new ActionListener () { public void actionPerformed (ActionEvent e) { bSelect.setEnabled (true); tfName.setEnabled (true); setValue (); updateIcon (); } }); rbClasspath.addActionListener (new ActionListener () { public void actionPerformed (ActionEvent e) { bSelect.setEnabled (true); tfName.setEnabled (true); setValue (); } }); rbNoPicture.addActionListener (new ActionListener () { public void actionPerformed (ActionEvent e) { bSelect.setEnabled (false); tfName.setEnabled (false); IconEditor.this.setValue (null); updateIcon (); } }); bSelect.addActionListener (new ActionListener () { public void actionPerformed (ActionEvent e) { if (rbFile.isSelected ()) { JFileChooser chooser = new JFileChooser (); FileFilter filter = new FileFilter () { public boolean accept (java.io.File f) { return f.isDirectory () || isImage (f.getName ()); } public String getDescription () { return bundle.getString ("CTL_ImagesExtensionName"); } }; chooser.setFileFilter (filter); chooser.setFileSelectionMode (JFileChooser.FILES_AND_DIRECTORIES ); int returnVal = chooser.showOpenDialog ( IconPanel.this ); if (returnVal != JFileChooser.APPROVE_OPTION) return; tfName.setText (chooser.getSelectedFile ().getAbsolutePath ()); setValue (); } else if (rbClasspath.isSelected ()) { // InputPanel ip = new InputPanel (); Places places = TopManager.getDefault ().getPlaces (); Node ds = places.nodes ().repository (new DataFilter () { public boolean acceptDataObject (DataObject obj) { // accept only data folders but ignore read only roots of file systems if (obj instanceof DataFolder) return !obj.getPrimaryFile ().isReadOnly () || obj.getPrimaryFile ().getParent () != null; return isImage (obj.getPrimaryFile ().getExt ()); } }); String name; try { // selects one folder from data systems DataObject d = (DataObject) TopManager.getDefault ().getNodeOperation ().select ( bundle.getString ("CTL_OpenDialogName"), bundle.getString ("CTL_FileSystemName"), TopManager.getDefault ().getPlaces ().nodes ().repository (), new NodeAcceptor () { public boolean acceptNodes (Node[] nodes) { if ((nodes == null) || (nodes.length != 1)) return false; return nodes[0].getCookie(DataFolder.class) == null; } }, null )[0].getCookie(DataObject.class); name = (d.getPrimaryFile ().getPackageNameExt ('/', '.')); } catch (org.openide.util.UserCancelException ex) { return; } tfName.setText ("/" + name); // NOI18N setValue (); } } }); // initialization ...................................... updateIcon (); Icon i = (Icon)getValue (); if (i == null) { rbNoPicture.setSelected (true); bSelect.setEnabled (false); tfName.setEnabled (false); return; } if (!(i instanceof NbImageIcon)) return; switch (((NbImageIcon)i).type) { case TYPE_URL: rbUrl.setSelected (true); bSelect.setEnabled (false); break; case TYPE_FILE: rbFile.setSelected (true); bSelect.setEnabled (true); break; case TYPE_CLASSPATH: rbClasspath.setSelected (true); bSelect.setEnabled (true); break; } tfName.setText (((NbImageIcon)i).name); HelpCtx.setHelpIDString (this, IconPanel.class.getName ()); } void updateIcon () { Icon i = (Icon)getValue (); spImage.setViewportView ((i == null) ? new JLabel () : new JLabel (i)); // repaint (); validate (); } void setValue () { String s = ""; // NOI18N if (rbUrl.isSelected ()) s = URL_PREFIX + ": "; // NOI18N else if (rbFile.isSelected ()) s = FILE_PREFIX + ": "; // NOI18N else if (rbClasspath.isSelected ()) s = CLASSPATH_PREFIX + ": "; // NOI18N try { setAsText (s + tfName.getText ()); } catch (IllegalArgumentException ee) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) ee.printStackTrace (); // NOI18N } updateIcon (); /* Icon i = (Icon) getValue (); if (i != null) { spImage.setViewportView (new JLabel (i)); repaint (); validate (); }*/ } /** * @return Returns the property value that is result of the CustomPropertyEditor. * @exception InvalidStateException when the custom property editor does not * represent valid property value (and thus it should not be set) */ public Object getPropertyValue () throws IllegalStateException { NbImageIcon ii = null; String s = tfName.getText().trim(); try { if (rbFile.isSelected ()) { ii = new NbImageIcon (s); ii.type = TYPE_FILE; ii.name = s; } else if (rbClasspath.isSelected ()) { URL url = TopManager.getDefault ().currentClassLoader ().getResource (s); ii = new NbImageIcon (url); ii.type = TYPE_CLASSPATH; ii.name = s; } else if (rbUrl.isSelected()) { URL url = new URL (s); ii = new NbImageIcon (url); ii.type = TYPE_URL; ii.name = s; } } catch (Exception e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace (); // NOI18N throw new IllegalStateException (e.toString ()); } return ii; } } // end of IconPanel //-------------------------------------------------------------------------- // XMLPropertyEditor implementation public static final String XML_IMAGE = "Image"; // NOI18N public static final String ATTR_TYPE = "iconType"; // NOI18N public static final String ATTR_NAME = "name"; // NOI18N /** Called to load property value from specified XML subtree. If succesfully loaded, * the value should be available via the getValue method. * An IOException should be thrown when the value cannot be restored from the specified XML element * @param element the XML DOM element representing a subtree of XML from which the value should be loaded * @exception IOException thrown when the value cannot be restored from the specified XML element */ public void readFromXML (org.w3c.dom.Node element) throws java.io.IOException { if (!XML_IMAGE.equals (element.getNodeName ())) { throw new java.io.IOException (); } org.w3c.dom.NamedNodeMap attributes = element.getAttributes (); try { int type = Integer.parseInt (attributes.getNamedItem (ATTR_TYPE).getNodeValue ()); String name = attributes.getNamedItem (ATTR_NAME).getNodeValue (); switch (type) { case TYPE_URL: setAsText (URL_PREFIX + ": " + name); break; // NOI18N case TYPE_FILE: setAsText (FILE_PREFIX + ": " + name); break; // NOI18N case TYPE_CLASSPATH: setAsText (CLASSPATH_PREFIX + ": " + name); break; // NOI18N } } catch (NullPointerException e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace (); // NOI18N throw new java.io.IOException (e.toString()); } } /** Called to store current property value into XML subtree. The property value should be set using the * setValue method prior to calling this method. * @param doc The XML document to store the XML in - should be used for creating nodes only * @return the XML DOM element representing a subtree of XML from which the value should be loaded */ public org.w3c.dom.Node storeToXML(org.w3c.dom.Document doc) { org.w3c.dom.Element el = doc.createElement (XML_IMAGE); NbImageIcon ii = (NbImageIcon)getValue (); el.setAttribute (ATTR_TYPE, Integer.toString(ii.type)); el.setAttribute (ATTR_NAME, ii.name); return el; } } /* * Log * 20 Gandalf 1.19 1/12/00 Ian Formanek * 19 Gandalf 1.18 1/12/00 Ian Formanek I18N * 18 Gandalf 1.17 1/12/00 Ian Formanek NOI18N * 17 Gandalf 1.16 12/9/99 Pavel Buzek * 16 Gandalf 1.15 11/24/99 Pavel Buzek XMLPropertyEditor * interface added * 15 Gandalf 1.14 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 14 Gandalf 1.13 8/17/99 Ian Formanek Fixed bug 3429 - Icon * propertiess are not supported yet in the forms editor correctly. * 13 Gandalf 1.12 8/9/99 Ian Formanek Generated Serial Version * UID * 12 Gandalf 1.11 7/8/99 Jesse Glick Context help. * 11 Gandalf 1.10 6/30/99 Ian Formanek Moved to package * org.openide.explorer.propertysheet.editors * 10 Gandalf 1.9 6/30/99 Ian Formanek Reflecting changes in * editors packages and enhanced property editor interfaces * 9 Gandalf 1.8 6/24/99 Jesse Glick Gosh-honest HelpID's. * 8 Gandalf 1.7 6/8/99 Ian Formanek ---- Package Change To * org.openide ---- * 7 Gandalf 1.6 4/27/99 Jesse Glick new HelpCtx () -> * HelpCtx.DEFAULT_HELP. * 6 Gandalf 1.5 3/4/99 Jan Jancura WindowToolkit removed * 5 Gandalf 1.4 3/4/99 Jan Jancura bundle moved * 4 Gandalf 1.3 2/20/99 David Simonek bugfix #1142 * 3 Gandalf 1.2 1/6/99 Jaroslav Tulach * 2 Gandalf 1.1 1/6/99 Jaroslav Tulach ide.* extended to * ide.loaders.* * 1 Gandalf 1.0 1/5/99 Ian Formanek * $ */